package service

import (
	"errors"
	"fmt"
	"github.com/digineo/go-uci"
	"net"
	"os/exec"
	"regexp"
	"serialnet/app"
	"serialnet/model"
	"serialnet/netty"
	"serialnet/netty/transport/tcp"
	"serialnet/protocol"
	"serialnet/sys/gpio"
	"strconv"
	"time"
)

type RuntimeService struct {
	running        bool
	rs232Ctx       *SerialContext
	rs232Bootstrap netty.Bootstrap
	rs485Ctx       *SerialContext
	rs485Bootstrap netty.Bootstrap
}

func NewRuntimeServer() *RuntimeService {
	return &RuntimeService{running: false}
}

func (r *RuntimeService) Start() error {
	if err := r.startRs232Task(); err != nil {
		app.Logger.Error("rs232 rx tx task failed to start:", err)
	} else {
		app.Logger.Info("rs232 rx tx task succeed to start!")
	}
	if err := r.startRs485Task(); err != nil {
		app.Logger.Error("rs485 rx tx task failed to start::", err)
	} else {
		app.Logger.Info("rs485 rx tx task succeed to start!!")
	}
	r.running = true
	go r.ViLanMonitor()
	return nil
}

func (r *RuntimeService) Stop() error {
	r.running = false
	if r.rs232Bootstrap != nil {
		r.rs232Bootstrap.Stop()
		r.rs232Bootstrap = nil
	}
	if r.rs485Bootstrap != nil {
		r.rs485Bootstrap.Stop()
		r.rs485Bootstrap = nil
	}
	if r.rs232Ctx != nil {
		_ = r.rs232Ctx.Stop()
		r.rs232Ctx = nil
	}
	if r.rs485Ctx != nil {
		_ = r.rs485Ctx.Stop()
		r.rs485Ctx = nil
	}
	return nil
}

func (r *RuntimeService) ViLanMonitor() {
	listen, err := net.ListenUDP("udp", &net.UDPAddr{
		IP:   net.IPv4(127, 0, 0, 1),
		Port: 30500,
	})
	if err != nil {
		app.Logger.Error("listen failed, err:", err)
		return
	}

	defer listen.Close()

	var linkLed, faultLed gpio.Pin
	if link, ok := uci.GetLast("vilan", "led", "link"); ok {
		reg := regexp.MustCompile("(\\d+)")
		iStr := reg.FindString(link)
		if v, e := strconv.ParseUint(iStr, 10, 16); e == nil {
			linkLed, err = gpio.OpenPin(int(v), gpio.ModeOutput)
			if linkLed == nil {
				app.Logger.Warn("net monitor link led open failed")
				return
			}
		} else {
			app.Logger.Warn("net monitor link led config load failed")
			return
		}
	} else {
		app.Logger.Warn("net monitor link led config load failed")
		return
	}
	if fault, ok := uci.GetLast("vilan", "led", "fault"); ok {
		reg := regexp.MustCompile("(\\d+)")
		iStr := reg.FindString(fault)
		if v, e := strconv.ParseUint(iStr, 10, 16); e == nil {
			faultLed, err = gpio.OpenPin(int(v), gpio.ModeOutput)
			if faultLed == nil {
				app.Logger.Warn("net monitor fault led open failed")
				return
			}
		} else {
			app.Logger.Warn("net monitor fault led config load failed")
			return
		}
	} else {
		app.Logger.Warn("net monitor fault led config load failed")
		return
	}
	var data [10]byte
	isLink := false
	lastLinkTime := time.Now().Add(-3 * time.Second)
	restartVilan := false
	linkLedState := false
	faultLedState := false
	notRecCount := 0
	startTime := time.Now().Unix()
	for r.running {
		_ = listen.SetReadDeadline(time.Now().Add(time.Second))
		n, _, err := listen.ReadFromUDP(data[:]) // 接收数据
		if err != nil {
			notRecCount++
			if notRecCount < 3 {
				continue
			}
		} else {
			notRecCount = 0
		}
		now := time.Now()
		if n == 5 {
			if data[2] != 0x00 {
				isLink = true
				lastLinkTime = now
			} else {
				isLink = false
			}
		} else {
			isLink = false
		}

		if isLink {
			if faultLedState {
				faultLedState = false
				faultLed.Clear()
			}
			if linkLedState {
				linkLed.Clear()
			} else {
				linkLed.Set()
			}
			linkLedState = !linkLedState
		} else {
			if now.Unix()-startTime < 30 {
				faultLed.Clear()
				linkLed.Clear()
				continue
			}
			if now.Sub(lastLinkTime).Seconds() > 65 {
				if !restartVilan {
					restartVilan = true
					_ = exec.Command("/etc/init.d/vilan", "restart").Start()
				} else if now.Sub(lastLinkTime).Seconds() > 300 {
					_ = exec.Command("reboot").Run()
				}
			} else {
				restartVilan = false
			}
			if linkLedState {
				linkLedState = false
				linkLed.Clear()
			}
			if faultLedState {
				faultLed.Clear()
			} else {
				faultLed.Set()
			}
			faultLedState = !faultLedState
		}
	}
}

func (r *RuntimeService) startRs232Task() error {
	if app.Config.Rs232Config != nil {
		ctx := NewSerialContext(true, r)
		ctx.Para = app.Config.Rs232Config
		if err := ctx.Start(); err != nil {
			app.Logger.Error("rs232 port start failed:", err)
		}
		r.rs232Ctx = ctx
		if r.rs232Bootstrap != nil {
			r.rs232Bootstrap.Stop()
			r.rs232Bootstrap = nil
		}
		defer func() {
			if err := recover(); err != nil {
				return
			}
		}()
		r.rs232Bootstrap = netty.NewBootstrap()
		// 子连接的流水线配置
		var childInitializer = func(channel netty.Channel) {
			channel.Pipeline().
				AddLast(protocol.NewMsgCodec(0)).
				AddLast(protocol.NewMsgHandler(true))
		}
		var option = &tcp.Options{
			Timeout:         time.Second * 5,
			KeepAlive:       true,
			KeepAlivePeriod: time.Minute, // tcp ping 等待间隔
			Linger:          0,
			NoDelay:         true,
		}
		// 创建Bootstrap & 监听端口 & 接受连接
		//r.bootstrap.Channel(netty.NewBufferedChannel(app.Config.PacketNum, int(app.Config.MaxPacketSize)))
		r.rs232Bootstrap.ChildInitializer(childInitializer).ClientInitializer(childInitializer)
		r.rs232Bootstrap.ChannelExecutor(netty.NewFlexibleChannelExecutor(10, 1, 10))
		r.rs232Bootstrap.Transport(tcp.New()).Listen(fmt.Sprintf(":%d", app.Config.Rs232Config.ListenPort), tcp.WithOptions(option))
		return nil
	} else {
		return errors.New("rs232 config is nil")
	}
}

func (r *RuntimeService) startRs485Task() error {
	if app.Config.Rs485Config != nil {
		ctx := NewSerialContext(false, r)
		ctx.Para = app.Config.Rs485Config
		if err := ctx.Start(); err != nil {
			app.Logger.Error("rs485 port start failed:", err)
		}
		r.rs485Ctx = ctx
		if r.rs485Bootstrap != nil {
			r.rs485Bootstrap.Stop()
			r.rs485Bootstrap = nil
		}
		defer func() {
			if err := recover(); err != nil {
				return
			}
		}()
		r.rs485Bootstrap = netty.NewBootstrap()
		// 子连接的流水线配置
		var childInitializer = func(channel netty.Channel) {
			channel.Pipeline().
				AddLast(protocol.NewMsgCodec(0)).
				AddLast(protocol.NewMsgHandler(false))
		}
		var option = &tcp.Options{
			Timeout:         time.Second * 5,
			KeepAlive:       true,
			KeepAlivePeriod: time.Minute, // tcp ping 等待间隔
			Linger:          0,
			NoDelay:         true,
		}
		// 创建Bootstrap & 监听端口 & 接受连接
		//r.bootstrap.Channel(netty.NewBufferedChannel(app.Config.PacketNum, int(app.Config.MaxPacketSize)))
		r.rs485Bootstrap.ChildInitializer(childInitializer).ClientInitializer(childInitializer)
		r.rs485Bootstrap.ChannelExecutor(netty.NewFlexibleChannelExecutor(10, 1, 10))
		r.rs485Bootstrap.Transport(tcp.New()).Listen(fmt.Sprintf(":%d", app.Config.Rs485Config.ListenPort), tcp.WithOptions(option))
		return nil
	} else {
		return errors.New("rs485 config is nil")
	}
}

func (r *RuntimeService) SetHandler(isRs232 bool, handler netty.HandlerContext) {
	if isRs232 {
		if r.rs232Ctx != nil {
			r.rs232Ctx.SetHandler(handler)
		}
	} else {
		if r.rs485Ctx != nil {
			r.rs485Ctx.SetHandler(handler)
		}
	}
}

func (r *RuntimeService) ProcessMessage(isRs232 bool, msg *model.MsgFrame) error {
	if isRs232 {
		if r.rs232Ctx != nil {
			return r.rs232Ctx.processMsg(msg)
		} else {
			return errors.New("rs232 context is nil")
		}
	} else {
		if r.rs485Ctx != nil {
			return r.rs485Ctx.processMsg(msg)
		} else {
			return errors.New("rs485 context is nil")
		}
	}
}
